/**
* 异步队列。使用when.js的实现。 https://github.com/cujojs/when
* 更多例子请网上查阅when.js的资料
* 顺序异步执行多个函数的例子：
* 注意：首先执行fun1，如果执行过程中调用了defer.reject则会跳到otherwise，否则调用defer.resolve()继续下一个then指明的fun2。
*
* var when = require("lib/util/when");
* var ajax = require("lib/io/ajax");
*
* var fun1 = function() {
*     var defer = when.defer();
*
*     ajax({
*         url: "/a.php",
*         method: "get",
*         onSuccess: function() {
*             defer.resolve();
*         },
*         onError: function() {
*             defer.reject("网络连接失败1");
*         }
*     })
*
*     return defer.promise; // 注意不管如何，函数在return时一定要返回这个对象
* }
*
* var fun2 = function() {
*     var defer = when.defer();
*
*     ajax({
*         url: "/b.php",
*         method: "get",
*         onSuccess: function() {
*             defer.resolve();
*         },
*         onError: function() {
*             defer.reject("网络连接失败2");
*         }
*     })
*
*     return defer.promise; // 注意不管如何，函数在return时一定要返回这个对象
* }
*
* var promise = fun1()
*     .then(fun2)
*     .then(function() {
*         console.log("前边的两个函数都调用了defer.resolve表示它们都执行成功，这里可以做最后的处理了")
*     })
*     .otherwise(function(msg) {
*         // 如果调用过程中没有调用defer.reject是不会走到这的
*         // msg是defer.reject传过来的值，它可以是对象
*         console.log("报错信息：" + msg);
*     });
*
*
*/
var global    = window;
/** @license MIT License (c) copyright 2011-2013 original author or authors */

/**
* A lightweight CommonJS Promises/A and when() implementation
* when is part of the cujo.js family of libraries (http://cujojs.com/)
*
* Licensed under the MIT License at:
* http://www.opensource.org/licenses/mit-license.php
*
* @author Brian Cavalier
* @author John Hann
* @version 2.5.1
*/

// Public API

when.promise   = promise;    // Create a pending promise
when.resolve   = resolve;    // Create a resolved promise
when.reject    = reject;     // Create a rejected promise
when.defer     = defer;      // Create a {promise, resolver} pair

when.join      = join;       // Join 2 or more promises

when.all       = all;        // Resolve a list of promises
when.map       = map;        // Array.map() for promises
when.reduce    = reduce;     // Array.reduce() for promises
when.settle    = settle;     // Settle a list of promises

when.any       = any;        // One-winner race
when.some      = some;       // Multi-winner race

when.isPromise = isPromiseLike;  // DEPRECATED: use isPromiseLike
when.isPromiseLike = isPromiseLike; // Is something promise-like, aka thenable

/**
 * Register an observer for a promise or immediate value.
 *
 * @param {*} promiseOrValue
 * @param {function?} [onFulfilled] callback to be called when promiseOrValue is
 *   successfully fulfilled.  If promiseOrValue is an immediate value, callback
 *   will be invoked immediately.
 * @param {function?} [onRejected] callback to be called when promiseOrValue is
 *   rejected.
 * @param {function?} [onProgress] callback to be called when progress updates
 *   are issued for promiseOrValue.
 * @returns {Promise} a new {@link Promise} that will complete with the return
 *   value of callback or errback or the completion value of promiseOrValue if
 *   callback and/or errback is not supplied.
 */
function when(promiseOrValue, onFulfilled, onRejected, onProgress) {
        // Get a trusted promise for the input promiseOrValue, and then
        // register promise handlers
        return cast(promiseOrValue).then(onFulfilled, onRejected, onProgress);
}

function cast(x) {
        return x instanceof Promise ? x : resolve(x);
}

/**
 * Trusted Promise constructor.  A Promise created from this constructor is
 * a trusted when.js promise.  Any other duck-typed promise is considered
 * untrusted.
 * @constructor
 * @param {function} sendMessage function to deliver messages to the promise's handler
 * @param {function?} inspect function that reports the promise's state
 * @name Promise
 */
function Promise(sendMessage, inspect) {
        this._message = sendMessage;
        this.inspect = inspect;
}

Promise.prototype = {
        /**
         * Register handlers for this promise.
         * @param [onFulfilled] {Function} fulfillment handler
         * @param [onRejected] {Function} rejection handler
         * @param [onProgress] {Function} progress handler
         * @return {Promise} new Promise
         */
        then: function(onFulfilled, onRejected, onProgress) {
                /*jshint unused:false*/
                var args, sendMessage;

                args = arguments;
                sendMessage = this._message;
                return _promise(function(resolve, reject, notify) {
                        sendMessage('when', args, resolve, notify);
                }, this._status && this._status.observed());
        },

        /**
         * Register a rejection handler.  Shortcut for .then(undefined, onRejected)
         * @param {function?} onRejected
         * @return {Promise}
         */
        otherwise: function(onRejected) {
                return this.then(undef, onRejected);
        },

        /**
         * Ensures that onFulfilledOrRejected will be called regardless of whether
         * this promise is fulfilled or rejected.  onFulfilledOrRejected WILL NOT
         * receive the promises' value or reason.  Any returned value will be disregarded.
         * onFulfilledOrRejected may throw or return a rejected promise to signal
         * an additional error.
         * @param {function} onFulfilledOrRejected handler to be called regardless of
         *  fulfillment or rejection
         * @returns {Promise}
         */
        ensure: function(onFulfilledOrRejected) {
                return typeof onFulfilledOrRejected === 'function'
                        ? this.then(injectHandler, injectHandler)['yield'](this)
                        : this;

                function injectHandler() {
                        return resolve(onFulfilledOrRejected());
                }
        },

        /**
         * Shortcut for .then(function() { return value; })
         * @param  {*} value
         * @return {Promise} a promise that:
         *  - is fulfilled if value is not a promise, or
         *  - if value is a promise, will fulfill with its value, or reject
         *    with its reason.
         */
        'yield': function(value) {
                return this.then(function() {
                        return value;
                });
        },

        /**
         * Runs a side effect when this promise fulfills, without changing the
         * fulfillment value.
         * @param {function} onFulfilledSideEffect
         * @returns {Promise}
         */
        tap: function(onFulfilledSideEffect) {
                return this.then(onFulfilledSideEffect)['yield'](this);
        },

        /**
         * Assumes that this promise will fulfill with an array, and arranges
         * for the onFulfilled to be called with the array as its argument list
         * i.e. onFulfilled.apply(undefined, array).
         * @param {function} onFulfilled function to receive spread arguments
         * @return {Promise}
         */
        spread: function(onFulfilled) {
                return this.then(function(array) {
                        // array may contain promises, so resolve its contents.
                        return all(array, function(array) {
                                return onFulfilled.apply(undef, array);
                        });
                });
        },

        /**
         * Shortcut for .then(onFulfilledOrRejected, onFulfilledOrRejected)
         * @deprecated
         */
        always: function(onFulfilledOrRejected, onProgress) {
                return this.then(onFulfilledOrRejected, onFulfilledOrRejected, onProgress);
        }
};

/**
 * Returns a resolved promise. The returned promise will be
 *  - fulfilled with promiseOrValue if it is a value, or
 *  - if promiseOrValue is a promise
 *    - fulfilled with promiseOrValue's value after it is fulfilled
 *    - rejected with promiseOrValue's reason after it is rejected
 * @param  {*} value
 * @return {Promise}
 */
function resolve(value) {
        return promise(function(resolve) {
                resolve(value);
        });
}

/**
 * Returns a rejected promise for the supplied promiseOrValue.  The returned
 * promise will be rejected with:
 * - promiseOrValue, if it is a value, or
 * - if promiseOrValue is a promise
 *   - promiseOrValue's value after it is fulfilled
 *   - promiseOrValue's reason after it is rejected
 * @param {*} promiseOrValue the rejected value of the returned {@link Promise}
 * @return {Promise} rejected {@link Promise}
 */
function reject(promiseOrValue) {
        return when(promiseOrValue, rejected);
}

/**
 * Creates a {promise, resolver} pair, either or both of which
 * may be given out safely to consumers.
 * The resolver has resolve, reject, and progress.  The promise
 * has then plus extended promise API.
 *
 * @return {{
 * promise: Promise,
 * resolve: function:Promise,
 * reject: function:Promise,
 * notify: function:Promise
 * resolver: {
 *        resolve: function:Promise,
 *        reject: function:Promise,
 *        notify: function:Promise
 * }}}
 */
function defer() {
        var deferred, pending, resolved;

        // Optimize object shape
        deferred = {
                promise: undef, resolve: undef, reject: undef, notify: undef,
                resolver: { resolve: undef, reject: undef, notify: undef }
        };

        deferred.promise = pending = promise(makeDeferred);

        return deferred;

        function makeDeferred(resolvePending, rejectPending, notifyPending) {
                deferred.resolve = deferred.resolver.resolve = function(value) {
                        if(resolved) {
                                return resolve(value);
                        }
                        resolved = true;
                        resolvePending(value);
                        return pending;
                };

                deferred.reject  = deferred.resolver.reject  = function(reason) {
                        if(resolved) {
                                return resolve(rejected(reason));
                        }
                        resolved = true;
                        rejectPending(reason);
                        return pending;
                };

                deferred.notify  = deferred.resolver.notify  = function(update) {
                        notifyPending(update);
                        return update;
                };
        }
}

/**
 * Creates a new promise whose fate is determined by resolver.
 * @param {function} resolver function(resolve, reject, notify)
 * @returns {Promise} promise whose fate is determine by resolver
 */
function promise(resolver) {
        return _promise(resolver, monitorApi.PromiseStatus && monitorApi.PromiseStatus());
}

/**
 * Creates a new promise, linked to parent, whose fate is determined
 * by resolver.
 * @param {function} resolver function(resolve, reject, notify)
 * @param {Promise?} status promise from which the new promise is begotten
 * @returns {Promise} promise whose fate is determine by resolver
 * @private
 */
function _promise(resolver, status) {
        var self, value, consumers = [];

        self = new Promise(_message, inspect);
        self._status = status;

        // Call the provider resolver to seal the promise's fate
        try {
                resolver(promiseResolve, promiseReject, promiseNotify);
        } catch(e) {
                promiseReject(e);
        }

        // Return the promise
        return self;

        /**
         * Private message delivery. Queues and delivers messages to
         * the promise's ultimate fulfillment value or rejection reason.
         * @private
         * @param {String} type
         * @param {Array} args
         * @param {Function} resolve
         * @param {Function} notify
         */
        function _message(type, args, resolve, notify) {
                consumers ? consumers.push(deliver) : enqueue(function() { deliver(value); });

                function deliver(p) {
                        p._message(type, args, resolve, notify);
                }
        }

        /**
         * Returns a snapshot of the promise's state at the instant inspect()
         * is called. The returned object is not live and will not update as
         * the promise's state changes.
         * @returns {{ state:String, value?:*, reason?:* }} status snapshot
         *  of the promise.
         */
        function inspect() {
                return value ? value.inspect() : toPendingState();
        }

        /**
         * Transition from pre-resolution state to post-resolution state, notifying
         * all listeners of the ultimate fulfillment or rejection
         * @param {*|Promise} val resolution value
         */
        function promiseResolve(val) {
                if(!consumers) {
                        return;
                }

                var queue = consumers;
                consumers = undef;

                enqueue(function () {
                        value = coerce(self, val);
                        if(status) {
                                updateStatus(value, status);
                        }
                        runHandlers(queue, value);
                });

        }

        /**
         * Reject this promise with the supplied reason, which will be used verbatim.
         * @param {*} reason reason for the rejection
         */
        function promiseReject(reason) {
                promiseResolve(rejected(reason));
        }

        /**
         * Issue a progress event, notifying all progress listeners
         * @param {*} update progress event payload to pass to all listeners
         */
        function promiseNotify(update) {
                if(consumers) {
                        var queue = consumers;
                        enqueue(function () {
                                runHandlers(queue, progressed(update));
                        });
                }
        }
}

/**
 * Run a queue of functions as quickly as possible, passing
 * value to each.
 */
function runHandlers(queue, value) {
        for (var i = 0; i < queue.length; i++) {
                queue[i](value);
        }
}

/**
 * Creates a fulfilled, local promise as a proxy for a value
 * NOTE: must never be exposed
 * @param {*} value fulfillment value
 * @returns {Promise}
 */
function fulfilled(value) {
        return near(
                new NearFulfilledProxy(value),
                function() { return toFulfilledState(value); }
        );
}

/**
 * Creates a rejected, local promise with the supplied reason
 * NOTE: must never be exposed
 * @param {*} reason rejection reason
 * @returns {Promise}
 */
function rejected(reason) {
        return near(
                new NearRejectedProxy(reason),
                function() { return toRejectedState(reason); }
        );
}

/**
 * Creates a near promise using the provided proxy
 * NOTE: must never be exposed
 * @param {object} proxy proxy for the promise's ultimate value or reason
 * @param {function} inspect function that returns a snapshot of the
 *  returned near promise's state
 * @returns {Promise}
 */
function near(proxy, inspect) {
        return new Promise(function (type, args, resolve) {
                try {
                        resolve(proxy[type].apply(proxy, args));
                } catch(e) {
                        resolve(rejected(e));
                }
        }, inspect);
}

/**
 * Create a progress promise with the supplied update.
 * @private
 * @param {*} update
 * @return {Promise} progress promise
 */
function progressed(update) {
        return new Promise(function (type, args, _, notify) {
                var onProgress = args[2];
                try {
                        notify(typeof onProgress === 'function' ? onProgress(update) : update);
                } catch(e) {
                        notify(e);
                }
        });
}

/**
 * Coerces x to a trusted Promise
 * @param {*} x thing to coerce
 * @returns {*} Guaranteed to return a trusted Promise.  If x
 *   is trusted, returns x, otherwise, returns a new, trusted, already-resolved
 *   Promise whose resolution value is:
 *   * the resolution value of x if it's a foreign promise, or
 *   * x if it's a value
 */
function coerce(self, x) {
        if (x === self) {
                return rejected(new TypeError());
        }

        if (x instanceof Promise) {
                return x;
        }

        try {
                var untrustedThen = x === Object(x) && x.then;

                return typeof untrustedThen === 'function'
                        ? assimilate(untrustedThen, x)
                        : fulfilled(x);
        } catch(e) {
                return rejected(e);
        }
}

/**
 * Safely assimilates a foreign thenable by wrapping it in a trusted promise
 * @param {function} untrustedThen x's then() method
 * @param {object|function} x thenable
 * @returns {Promise}
 */
function assimilate(untrustedThen, x) {
        return promise(function (resolve, reject) {
                fcall(untrustedThen, x, resolve, reject);
        });
}

/**
 * Proxy for a near, fulfilled value
 * @param {*} value
 * @constructor
 */
function NearFulfilledProxy(value) {
        this.value = value;
}

NearFulfilledProxy.prototype.when = function(onResult) {
        return typeof onResult === 'function' ? onResult(this.value) : this.value;
};

/**
 * Proxy for a near rejection
 * @param {*} reason
 * @constructor
 */
function NearRejectedProxy(reason) {
        this.reason = reason;
}

NearRejectedProxy.prototype.when = function(_, onError) {
        if(typeof onError === 'function') {
                return onError(this.reason);
        } else {
                throw this.reason;
        }
};

function updateStatus(value, status) {
        value.then(statusFulfilled, statusRejected);

        function statusFulfilled() { status.fulfilled(); }
        function statusRejected(r) { status.rejected(r); }
}

/**
 * Determines if x is promise-like, i.e. a thenable object
 * NOTE: Will return true for *any thenable object*, and isn't truly
 * safe, since it may attempt to access the `then` property of x (i.e.
 *  clever/malicious getters may do weird things)
 * @param {*} x anything
 * @returns {boolean} true if x is promise-like
 */
function isPromiseLike(x) {
        return x && typeof x.then === 'function';
}

/**
 * Initiates a competitive race, returning a promise that will resolve when
 * howMany of the supplied promisesOrValues have resolved, or will reject when
 * it becomes impossible for howMany to resolve, for example, when
 * (promisesOrValues.length - howMany) + 1 input promises reject.
 *
 * @param {Array} promisesOrValues array of anything, may contain a mix
 *      of promises and values
 * @param howMany {number} number of promisesOrValues to resolve
 * @param {function?} [onFulfilled] DEPRECATED, use returnedPromise.then()
 * @param {function?} [onRejected] DEPRECATED, use returnedPromise.then()
 * @param {function?} [onProgress] DEPRECATED, use returnedPromise.then()
 * @returns {Promise} promise that will resolve to an array of howMany values that
 *  resolved first, or will reject with an array of
 *  (promisesOrValues.length - howMany) + 1 rejection reasons.
 */
function some(promisesOrValues, howMany, onFulfilled, onRejected, onProgress) {

        return when(promisesOrValues, function(promisesOrValues) {

                return promise(resolveSome).then(onFulfilled, onRejected, onProgress);

                function resolveSome(resolve, reject, notify) {
                        var toResolve, toReject, values, reasons, fulfillOne, rejectOne, len, i;

                        len = promisesOrValues.length >>> 0;

                        toResolve = Math.max(0, Math.min(howMany, len));
                        values = [];

                        toReject = (len - toResolve) + 1;
                        reasons = [];

                        // No items in the input, resolve immediately
                        if (!toResolve) {
                                resolve(values);

                        } else {
                                rejectOne = function(reason) {
                                        reasons.push(reason);
                                        if(!--toReject) {
                                                fulfillOne = rejectOne = identity;
                                                reject(reasons);
                                        }
                                };

                                fulfillOne = function(val) {
                                        // This orders the values based on promise resolution order
                                        values.push(val);
                                        if (!--toResolve) {
                                                fulfillOne = rejectOne = identity;
                                                resolve(values);
                                        }
                                };

                                for(i = 0; i < len; ++i) {
                                        if(i in promisesOrValues) {
                                                when(promisesOrValues[i], fulfiller, rejecter, notify);
                                        }
                                }
                        }

                        function rejecter(reason) {
                                rejectOne(reason);
                        }

                        function fulfiller(val) {
                                fulfillOne(val);
                        }
                }
        });
}

/**
 * Initiates a competitive race, returning a promise that will resolve when
 * any one of the supplied promisesOrValues has resolved or will reject when
 * *all* promisesOrValues have rejected.
 *
 * @param {Array|Promise} promisesOrValues array of anything, may contain a mix
 *      of {@link Promise}s and values
 * @param {function?} [onFulfilled] DEPRECATED, use returnedPromise.then()
 * @param {function?} [onRejected] DEPRECATED, use returnedPromise.then()
 * @param {function?} [onProgress] DEPRECATED, use returnedPromise.then()
 * @returns {Promise} promise that will resolve to the value that resolved first, or
 * will reject with an array of all rejected inputs.
 */
function any(promisesOrValues, onFulfilled, onRejected, onProgress) {

        function unwrapSingleResult(val) {
                return onFulfilled ? onFulfilled(val[0]) : val[0];
        }

        return some(promisesOrValues, 1, unwrapSingleResult, onRejected, onProgress);
}

/**
 * Return a promise that will resolve only once all the supplied promisesOrValues
 * have resolved. The resolution value of the returned promise will be an array
 * containing the resolution values of each of the promisesOrValues.
 * @memberOf when
 *
 * @param {Array|Promise} promisesOrValues array of anything, may contain a mix
 *      of {@link Promise}s and values
 * @param {function?} [onFulfilled] DEPRECATED, use returnedPromise.then()
 * @param {function?} [onRejected] DEPRECATED, use returnedPromise.then()
 * @param {function?} [onProgress] DEPRECATED, use returnedPromise.then()
 * @returns {Promise}
 */
function all(promisesOrValues, onFulfilled, onRejected, onProgress) {
        return _map(promisesOrValues, identity).then(onFulfilled, onRejected, onProgress);
}

/**
 * Joins multiple promises into a single returned promise.
 * @return {Promise} a promise that will fulfill when *all* the input promises
 * have fulfilled, or will reject when *any one* of the input promises rejects.
 */
function join(/* ...promises */) {
        return _map(arguments, identity);
}

/**
 * Settles all input promises such that they are guaranteed not to
 * be pending once the returned promise fulfills. The returned promise
 * will always fulfill, except in the case where `array` is a promise
 * that rejects.
 * @param {Array|Promise} array or promise for array of promises to settle
 * @returns {Promise} promise that always fulfills with an array of
 *  outcome snapshots for each input promise.
 */
function settle(array) {
        return _map(array, toFulfilledState, toRejectedState);
}

/**
 * Promise-aware array map function, similar to `Array.prototype.map()`,
 * but input array may contain promises or values.
 * @param {Array|Promise} array array of anything, may contain promises and values
 * @param {function} mapFunc map function which may return a promise or value
 * @returns {Promise} promise that will fulfill with an array of mapped values
 *  or reject if any input promise rejects.
 */
function map(array, mapFunc) {
        return _map(array, mapFunc);
}

/**
 * Internal map that allows a fallback to handle rejections
 * @param {Array|Promise} array array of anything, may contain promises and values
 * @param {function} mapFunc map function which may return a promise or value
 * @param {function?} fallback function to handle rejected promises
 * @returns {Promise} promise that will fulfill with an array of mapped values
 *  or reject if any input promise rejects.
 */
function _map(array, mapFunc, fallback) {
        return when(array, function(array) {

                return _promise(resolveMap);

                function resolveMap(resolve, reject, notify) {
                        var results, len, toResolve, i;

                        // Since we know the resulting length, we can preallocate the results
                        // array to avoid array expansions.
                        toResolve = len = array.length >>> 0;
                        results = [];

                        if(!toResolve) {
                                resolve(results);
                                return;
                        }

                        // Since mapFunc may be async, get all invocations of it into flight
                        for(i = 0; i < len; i++) {
                                if(i in array) {
                                        resolveOne(array[i], i);
                                } else {
                                        --toResolve;
                                }
                        }

                        function resolveOne(item, i) {
                                when(item, mapFunc, fallback).then(function(mapped) {
                                        results[i] = mapped;

                                        if(!--toResolve) {
                                                resolve(results);
                                        }
                                }, reject, notify);
                        }
                }
        });
}

/**
 * Traditional reduce function, similar to `Array.prototype.reduce()`, but
 * input may contain promises and/or values, and reduceFunc
 * may return either a value or a promise, *and* initialValue may
 * be a promise for the starting value.
 *
 * @param {Array|Promise} promise array or promise for an array of anything,
 *      may contain a mix of promises and values.
 * @param {function} reduceFunc reduce function reduce(currentValue, nextValue, index, total),
 *      where total is the total number of items being reduced, and will be the same
 *      in each call to reduceFunc.
 * @returns {Promise} that will resolve to the final reduced value
 */
function reduce(promise, reduceFunc /*, initialValue */) {
        var args = fcall(slice, arguments, 1);

        return when(promise, function(array) {
                var total;

                total = array.length;

                // Wrap the supplied reduceFunc with one that handles promises and then
                // delegates to the supplied.
                args[0] = function (current, val, i) {
                        return when(current, function (c) {
                                return when(val, function (value) {
                                        return reduceFunc(c, value, i, total);
                                });
                        });
                };

                return reduceArray.apply(array, args);
        });
}

// Snapshot states

/**
 * Creates a fulfilled state snapshot
 * @private
 * @param {*} x any value
 * @returns {{state:'fulfilled',value:*}}
 */
function toFulfilledState(x) {
        return { state: 'fulfilled', value: x };
}

/**
 * Creates a rejected state snapshot
 * @private
 * @param {*} x any reason
 * @returns {{state:'rejected',reason:*}}
 */
function toRejectedState(x) {
        return { state: 'rejected', reason: x };
}

/**
 * Creates a pending state snapshot
 * @private
 * @returns {{state:'pending'}}
 */
function toPendingState() {
        return { state: 'pending' };
}

//
// Internals, utilities, etc.
//

var reduceArray, slice, fcall, nextTick, handlerQueue,
        setTimeout, funcProto, call, arrayProto, monitorApi,
        cjsRequire, MutationObserver, undef;

// cjsRequire = require;

//
// Shared handler queue processing
//
// Credit to Twisol (https://github.com/Twisol) for suggesting
// this type of extensible queue + trampoline approach for
// next-tick conflation.

handlerQueue = [];

/**
 * Enqueue a task. If the queue is not currently scheduled to be
 * drained, schedule it.
 * @param {function} task
 */
function enqueue(task) {
        if(handlerQueue.push(task) === 1) {
                nextTick(drainQueue);
        }
}

/**
 * Drain the handler queue entirely, being careful to allow the
 * queue to be extended while it is being processed, and to continue
 * processing until it is truly empty.
 */
function drainQueue() {
        runHandlers(handlerQueue);
        handlerQueue = [];
}

// capture setTimeout to avoid being caught by fake timers
// used in time based tests
setTimeout = global.setTimeout;

// Allow attaching the monitor to when() if env has no console
monitorApi = typeof console != 'undefined' ? console : when;

// Sniff "best" async scheduling option
// Prefer process.nextTick or MutationObserver, then check for
// vertx and finally fall back to setTimeout
/*global process*/
if (typeof process === 'object' && process.nextTick) {
        nextTick = process.nextTick;
} else if(MutationObserver = global.MutationObserver || global.WebKitMutationObserver) {
        nextTick = (function(document, MutationObserver, drainQueue) {
                var el = document.createElement('div');
                new MutationObserver(drainQueue).observe(el, { attributes: true });

                return function() {
                        el.setAttribute('x', 'x');
                };
        }(document, MutationObserver, drainQueue));
} else {
        // try {
        //         // vert.x 1.x || 2.x
        //         nextTick = cjsRequire('vertx').runOnLoop || cjsRequire('vertx').runOnContext;
        // } catch(ignore) {
                nextTick = function(t) { setTimeout(t, 0); };
        // }
}

//
// Capture/polyfill function and array utils
//

// Safe function calls
funcProto = Function.prototype;
call = funcProto.call;
fcall = funcProto.bind
        ? call.bind(call)
        : function(f, context) {
                return f.apply(context, slice.call(arguments, 2));
        };

// Safe array ops
arrayProto = [];
slice = arrayProto.slice;

// ES5 reduce implementation if native not available
// See: http://es5.github.com/#x15.4.4.21 as there are many
// specifics and edge cases.  ES5 dictates that reduce.length === 1
// This implementation deviates from ES5 spec in the following ways:
// 1. It does not check if reduceFunc is a Callable
reduceArray = arrayProto.reduce ||
        function(reduceFunc /*, initialValue */) {
                /*jshint maxcomplexity: 7*/
                var arr, args, reduced, len, i;

                i = 0;
                arr = Object(this);
                len = arr.length >>> 0;
                args = arguments;

                // If no initialValue, use first item of array (we know length !== 0 here)
                // and adjust i to start at second item
                if(args.length <= 1) {
                        // Skip to the first real element in the array
                        for(;;) {
                                if(i in arr) {
                                        reduced = arr[i++];
                                        break;
                                }

                                // If we reached the end of the array without finding any real
                                // elements, it's a TypeError
                                if(++i >= len) {
                                        throw new TypeError();
                                }
                        }
                } else {
                        // If initialValue provided, use it
                        reduced = args[1];
                }

                // Do the actual reduce
                for(;i < len; ++i) {
                        if(i in arr) {
                                reduced = reduceFunc(reduced, arr[i], i, arr);
                        }
                }

                return reduced;
        };

function identity(x) {
        return x;
}

module.exports = when;
